package pip.can;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;

public class Node {
	private String nodeId;
	private String forward;
	private int Cache;
	private int ContentSize;
	private int QueueIndicator;
	private LinkedList<Chunk> Contents;
	private Queue[] Buffers;
	public boolean busy;
	
	public String getForward(){
		return forward;
	}
	public Node(String nodeId,int c, String forward)
	{
		Buffers= new Queue[StaticVariables.Classes];
		for(int i=0; i<StaticVariables.Classes; i++)
		{
			Buffers[i]=new Queue();
		}
		this.nodeId = nodeId;
		Contents = new LinkedList<Chunk>();
		this.forward=forward;
		Cache=c;
		ContentSize=0;
		busy=false;
	}
	public Chunk findContent(int chunkId, int contentId)
	{

		
//		System.out.println("szukam contentu w węźle "+this.nodeId);
		for(int i=0; i<Contents.size(); i++)
		{
			if((Contents.get(i).ChunkId== chunkId) && (Contents.get(i).ContentId== contentId))
			{
				Contents.add(Contents.remove(i));
				return Contents.getLast();
			}
		}
		return null;
	}
	public void addContent(Chunk ch)
	{
//		System.out.println("dodaje content do węzła "+this.nodeId);
		for(int i=0; i<Contents.size(); i++)
		{
			if((Contents.get(i).ChunkId== ch.ChunkId) && (Contents.get(i).ContentId== ch.ContentId))
			{
				return;
			}
		}
		if(ch.getSize()<=(Cache-ContentSize))
		{
			Contents.add(ch);
			ContentSize+=ch.getSize();
		}
		else
		{
			while(ch.getSize()>(Cache-ContentSize))
			{
				ContentSize-=Contents.removeFirst().getSize();
			}
			Contents.add(ch);
			ContentSize+=ch.getSize();
		}
	}
	public boolean buffersempty()
	{
		for(int i=0; i<Buffers.length; i++)
		{
			if(!Buffers[i].isEmpty())
			{
				return false;
			}
		}
		return true;
	}
	public double deliveryTime(String from, String to, int s)
	{
		double linkd=(double)StaticVariables.linkDelay*1e-6;
		double serviced=((double)s/((double)Path.getPathCapacity(from, to)))*1e-3;
		return linkd+serviced;
	}
	public void addPacket(Message mes, Simulator sim)
	{
		Buffers[mes.trafficClass].addPacket(mes);
		if(busy==false)
		{
			sim.addEvent(new Event(EventType.Departure,sim.time,this.nodeId,null));
			busy=true;
		}
		else
		{
//			System.out.println("wezeł "+this.nodeId+" jest busy");
		}
	}
	public void sendPacket(HashMap<String, Node> topology, Simulator sim)
	{
		int empty = 0;
		Message mes;
		do{
			if(!Buffers[this.QueueIndicator].isEmpty())
			{
				mes=Buffers[this.QueueIndicator].getPacket();
				if(mes!=null)
				{
//					System.out.println("W węźle "+this.nodeId+" jest coś do wysłania z kolejki "+mes.trafficClass+ " contentId= "+mes.ContentId+" ChunkId="+mes.ChunkId);
					if(mes.size!=0)
					{
						String back=mes.getback();
						if(back!=null)
						{
							this.addContent(mes.Chunk);
//							System.out.println("wiadomość będzie wracać do "+back);
							if(this.buffersempty())
							{
								sim.addEvent(new Event(EventType.BufferEmpty,sim.time+this.deliveryTime(nodeId, back, mes.size),this.nodeId,null));
							}
							else
							{
								sim.addEvent(new Event(EventType.Departure,sim.time+this.deliveryTime(nodeId, back, mes.size),this.nodeId,null));
							}
							sim.addEvent(new Event(EventType.Arrival,sim.time+this.deliveryTime(nodeId, back, mes.size),back,mes));
							//topology.get(back).addPacket(mes);
						}
						else
						{
							if(mes.ChunkId!=mes.noChunks-1)
							{
								if(this.buffersempty())
								{
									busy=false;
								}
								else
								{
									sim.addEvent(new Event(EventType.Departure,sim.time,this.nodeId,null));
								}
								sim.addEvent(new Event(EventType.Arrival,sim.time,this.nodeId,new Message(mes.ContentId, mes.ChunkId+1, mes.trafficClass, mes.requestTime)));
							}
							else
							{
//								System.out.println("pobrano cały content nr "+ mes.ContentId);
//								System.out.println("zajeło to "+(sim.time-mes.requestTime));
								sim.addResult(sim.time-mes.requestTime, mes.trafficClass);
								if(this.buffersempty())
								{
									busy=false;
								}
								else
								{
									sim.addEvent(new Event(EventType.Departure,sim.time,this.nodeId,null));
								}
							}
						}
					}
					else
					{
						if(topology.get(this.getForward())!=null)
						{
							Chunk tmp=this.findContent(mes.ChunkId, mes.ContentId);
							if(tmp==null)
							{
								mes.addtrace(this.nodeId);
//								System.out.println("bede forward wysyłał do węzła "+topology.get(this.getForward()).nodeId);
								sim.addEvent(new Event(EventType.Arrival,sim.time+this.deliveryTime(nodeId, this.forward, mes.size),this.forward,mes));
								if(this.buffersempty())
								{
									sim.addEvent(new Event(EventType.BufferEmpty,sim.time+this.deliveryTime(nodeId, forward, mes.size),this.nodeId,null));
								}
								else
								{
									sim.addEvent(new Event(EventType.Departure,sim.time+this.deliveryTime(nodeId, forward, mes.size),this.nodeId,null));
								}
								//topology.get(this.forward).addPacket(mes);
							}
							else
							{
								mes.Chunk=tmp;
								mes.size=tmp.getSize();
								mes.noChunks=StaticVariables.Repository.getNoChunks(tmp.ContentId, tmp.ChunkId);
								String back=mes.getback();
//								System.out.println("znalazlem content, bede wracał do "+back);
								sim.addEvent(new Event(EventType.Arrival,sim.time+this.deliveryTime(nodeId, back, mes.size),back,mes));
								if(this.buffersempty())
								{
									sim.addEvent(new Event(EventType.BufferEmpty,sim.time+this.deliveryTime(nodeId, back, mes.size),this.nodeId,null));
								}
								else
								{
									sim.addEvent(new Event(EventType.Departure,sim.time+this.deliveryTime(nodeId, back, mes.size),this.nodeId,null));
								}
								//topology.get(back).addPacket(mes);
							
							}
						}
						else
						{
//							System.out.println("dotarłem do repozytorium "+this.nodeId);
							Chunk tmp=StaticVariables.Repository.getChunk(mes.ContentId, mes.ChunkId);
							mes.size=tmp.getSize();
							mes.noChunks=StaticVariables.Repository.getNoChunks(mes.ContentId, mes.ChunkId);
							String back=mes.getback();
							mes.Chunk=tmp;
//							System.out.println("pobrałem z repozytorium i bede wysyłał backward do "+back);
							sim.addEvent(new Event(EventType.Arrival,sim.time+this.deliveryTime(nodeId, back, mes.size),back,mes));
							if(this.buffersempty())
							{
								sim.addEvent(new Event(EventType.BufferEmpty,sim.time+this.deliveryTime(nodeId, back, mes.size),this.nodeId,null));
							}
							else
							{
								sim.addEvent(new Event(EventType.Departure,sim.time+this.deliveryTime(nodeId, back, mes.size),this.nodeId,null));
							}
							//topology.get(back).addPacket(mes);
							
						}
					}
					this.QueueIndicator=(this.QueueIndicator+1)%StaticVariables.Classes;
					return;
				}
				else
				{
					empty=0;				
				}
			}
			else
			{
				empty++;
			}
			this.QueueIndicator=(this.QueueIndicator+1)%StaticVariables.Classes;
		}
		while(empty!=StaticVariables.Classes);
		System.out.println("kolejka jest pusta");
	}
}
